package com.googlecode.placesapiclient.client.util;

import com.googlecode.placesapiclient.client.entity.MatchedSubstring;
import com.googlecode.placesapiclient.client.entity.Place;
import com.googlecode.placesapiclient.client.entity.PlaceDetails;
import com.googlecode.placesapiclient.client.entity.PlacePrediction;
import com.googlecode.placesapiclient.client.exception.ErrorCode;
import com.googlecode.placesapiclient.client.exception.ErrorCodeException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

/**
 */
public class Converter {

    private static final org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(Converter.class.getName());

    public static Place convertJsonToPlace(JSONObject jsonObject) throws ErrorCodeException {
        try {
            Place result = new Place();
            JSONObject geometry = (JSONObject) jsonObject.get("geometry");
            JSONObject location = (JSONObject) geometry.get("location");

            if (jsonObject.has("opening_hours")) {
                JSONObject openingHours = (JSONObject) jsonObject.get("opening_hours");
                if (openingHours.has("open_now")) {
                    result.getOpeningHours().setOpenNow(openingHours.getBoolean("open_now"));
                }
            }

            result.setLatitude(location.getDouble("lat"));
            result.setLongitude(location.getDouble("lng"));
            if (jsonObject.has("icon")) {
                result.setIcon(jsonObject.getString("icon"));
            }
            if (jsonObject.has("name")) {
                result.setName(jsonObject.getString("name"));
            }
            if (jsonObject.has("vicinity")) {
                result.setVicinity(jsonObject.getString("vicinity"));
            }
            if (jsonObject.has("id")) {
                result.setId(jsonObject.getString("id"));
            }
            if (jsonObject.has("rating")) {
                result.setRating(jsonObject.getDouble("rating"));
            }
            if (jsonObject.has("price_level")) {
                result.setPriceLevel(jsonObject.getInt("price_level"));
            }

            result.getTypeList().addAll(convertTypeList(jsonObject));

            return result;
        } catch (JSONException e) {
            logger.error("JSONException during converting response", e);
            throw new ErrorCodeException(ErrorCode.CONVERSION_ERROR, e.getLocalizedMessage());
        }

    }


    public static PlaceDetails convertJsonToPlaceDetails(JSONObject jsonObject) throws ErrorCodeException {
        try {
            PlaceDetails result = new PlaceDetails();
            JSONObject geometry = (JSONObject) jsonObject.get("geometry");
            JSONObject location = (JSONObject) geometry.get("location");

            if (jsonObject.has("opening_hours")) {
                JSONObject openingHours = (JSONObject) jsonObject.get("opening_hours");
                if (openingHours.has("open_now")) {
                    result.getOpeningHours().setOpenNow(openingHours.getBoolean("open_now"));
                }
            }

            convertAddressComponents(jsonObject, result);

            result.setLatitude(location.getDouble("lat"));
            result.setLongitude(location.getDouble("lng"));
            if (jsonObject.has("icon")) {
                result.setIcon(jsonObject.getString("icon"));
            }
            if (jsonObject.has("name")) {
                result.setName(jsonObject.getString("name"));
            }
            result.setVicinity(jsonObject.getString("vicinity"));
            result.setId(jsonObject.getString("id"));

            if (jsonObject.has("rating")) {
                result.setRating(jsonObject.getDouble("rating"));
            }

            if (jsonObject.has("price_level")) {
                result.setPriceLevel(jsonObject.getInt("price_level"));
            }

            result.getTypeList().addAll(convertTypeList(jsonObject));

            return result;
        } catch (JSONException e) {
            logger.error("JSONException during converting response", e);
            throw new ErrorCodeException(ErrorCode.CONVERSION_ERROR, e.getLocalizedMessage());
        }
    }

    private static List<String> convertTypeList(JSONObject jsonObject) throws JSONException {
        List<String> types = new ArrayList<String>();
        if (jsonObject.has("types")) {
            JSONArray typesArray = jsonObject.getJSONArray("types");
            for (int i = 0; i < typesArray.length(); i++) {
                types.add(typesArray.getString(i));
            }
        }
        return types;
    }

    private static void convertAddressComponents(JSONObject jsonObject, PlaceDetails result) throws JSONException {
        if (jsonObject.has("address_components")) {

            JSONArray addressComponents = jsonObject.getJSONArray("address_components");

            for (int i = 0; i < addressComponents.length(); i++) {
                JSONObject addressComponent = addressComponents.getJSONObject(i);

                if (addressComponent.has("types")) {
                    JSONArray addressComponentTypes = addressComponent.getJSONArray("types");
                    String longName = addressComponent.getString("long_name");
                    String shortName = addressComponent.getString("short_name");
                    for (int j = 0; j < addressComponentTypes.length(); j++) {
                        result.getAddressComponentMap().put(addressComponentTypes.getString(j) + PlaceDetails.LONG_SUFFIX, longName);
                        result.getAddressComponentMap().put(addressComponentTypes.getString(j) + PlaceDetails.SHORT_SUFFIX, shortName);

                    }
                }
            }
        }
    }

    public static PlacePrediction convertJsonToPlacePrediction(JSONObject jsonObject) throws JSONException {
        PlacePrediction result = new PlacePrediction();
        result.setDescription(jsonObject.getString("description"));
        result.setId(jsonObject.getString("id"));
        result.setReference(jsonObject.getString("reference"));

        if (jsonObject.has("terms")) {
            JSONArray terms = jsonObject.getJSONArray("terms");
            for (int i = 0; i < terms.length(); i++) {

                Integer offset = terms.getJSONObject(i).getInt("offset");
                String value = terms.getJSONObject(i).getString("value");
                result.getTermsMap().put(value, offset);

            }
        }

        if (jsonObject.has("matched_substrings")) {
            JSONArray matchedSubstrings = jsonObject.getJSONArray("matched_substrings");
            for (int i = 0; i < matchedSubstrings.length(); i++) {

                Integer offset = matchedSubstrings.getJSONObject(i).getInt("offset");
                Integer length = matchedSubstrings.getJSONObject(i).getInt("length");
                result.getMatchedSubstringList().add(new MatchedSubstring(length, offset));

            }
        }

        result.getTypeList().addAll(convertTypeList(jsonObject));

        return result;
    }
}
